﻿using System;
using nRoute.Components.Composition;
using nRoute.Components.Messaging;
using nRoute.Utilities;

namespace nRoute.Mapping
{
    public class DefaultChannelObserverLocator
		 : ResourceLocator
    {
        private const string MUST_BE_IMPLEMENTATION_TYPE = "{0} type must be an implementation of {1}.";
        private static readonly Type CHANNEL_GENETIC_TYPE = typeof(IObservable<>);

        private readonly string _observerName;
        private readonly string _channelKey;
        private readonly Type _channelType;
        private readonly ThreadOption _threadOption;
        private readonly InitializationMode _initializationMode;
        private IDisposable _channelDisposer;

        public DefaultChannelObserverLocator(string observerName, Type observerType, Type channelType, string channelKey, ThreadOption threadOption, 
            ILifetimeManager lifetimeManager, InitializationMode initializationMode)
            : base(channelType.IsGenericType() ? channelType : CHANNEL_GENETIC_TYPE.MakeGenericType(channelType), observerType, lifetimeManager)
        {
            Guard.ArgumentIsAssignable(base.ResourceType, observerType, MUST_BE_IMPLEMENTATION_TYPE, base.ResourceType.FullName, observerType.FullName);

            _observerName = (string.IsNullOrEmpty(observerName)) ? observerType.FullName : observerName;
            _channelType = (channelType.IsGenericType()) ? channelType.GetGenericArguments()[0] : channelType;
            _channelKey = channelKey;
            _threadOption = threadOption;
            _initializationMode = initializationMode;
        
            if (initializationMode == InitializationMode.WhenAvailable)
            {
                throw new NotImplementedException();
            }
        }

#region Properties

        public string ObserverName
        {
            get { return _observerName; }
        }

        public Type ChannelType
        {
            get { return _channelType; }
        }

        public string ChannelKey
        {
            get { return _channelKey; }
        }

        public InitializationMode InitializationMode
        {
            get { return _initializationMode; }
        }

        public ThreadOption ThreadOption
        {
            get { return _threadOption; }
        }

#endregion

#region Overrides

        protected override object CreateResourceInstance(ITypeBuilder typeBuilder)
        {
            var _channel = base.CreateResourceInstance(typeBuilder);
            if (string.IsNullOrEmpty(this.ChannelKey))
            {
                _channelDisposer = Channel.Subscribe(this.ChannelType, _channel, this.ThreadOption, false);
            }
            else
            {
                _channelDisposer = Channel.Subscribe(this.ChannelType, this.ChannelKey, _channel, this.ThreadOption, false);
            }

            return _channel;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                 if (_channelDisposer != null) _channelDisposer.Dispose();
                _channelDisposer = null;
            }
        }

#endregion

        ~DefaultChannelObserverLocator() { Dispose(false); }

    }
}